import 'dart:async';
import 'dart:io';

import 'package:audio_session/audio_session.dart';
import 'package:av_audio_session/av_audio_session.dart';

import '../../bean/bean.dart' as bean;
import '../util.dart';

typedef OnDevicesChanged = void Function(bean.AudioDevicesChangedEvent event);
typedef OnDevices = void Function(Set<bean.AudioDevice> devices);

class AudioSessionUtil{
  AudioSessionUtil._();
  static final AudioSessionUtil _audioSessionUtil = AudioSessionUtil._();
  static AudioSessionUtil get instance => _audioSessionUtil;

  AudioSession? _audioSession;

  StreamSubscription? _devicesChangedSubs, _devicesSubs;

  /*void enableLog({required bool enable}){
    Log.enableLog = enable;
  }*/

  bool _hasInit = false;

  List<OnDevicesChanged?> onDevicesChangedList = [];
  List<OnDevices?> onDevicesList = [];

  Type? handlerType;

  // OnDevices? _customOnDevices;
  Future<void> initAudioSession({
    OnDevicesChanged? onDevicesChanged,
    OnDevices? onDevices,
    Type? handlerType,
  }) async{
    this.handlerType = handlerType;
    // _customOnDevices = onDevices;
    Log.log('$handlerType AudioSessionUtil _hasInit $_hasInit');
    if(!_hasInit){
      _hasInit = true;

      if(Platform.isIOS){
        bool result = await AvAudioSession.setAppleAudioConfiguration(
            configuration: AppleAudioConfiguration(
                appleAudioCategory: AppleAudioCategory.playAndRecord,
                appleAudioCategoryOptions: {
                  AppleAudioCategoryOption.allowBluetooth,
                  AppleAudioCategoryOption.allowBluetoothA2DP
                },
                appleAudioMode: AppleAudioMode.videoChat
            )
        );
        Log.log('$handlerType setAppleAudioConfiguration $result');

        // might wait for 1s to make sure config above really activated
        await Future.delayed(const Duration(seconds: 1));
      }

      Log.log('$handlerType _audioSession is null ? ${_audioSession == null}');
      _audioSession ??= await AudioSession.instance;
      _devicesChangedSubs = _audioSession?.devicesChangedEventStream
          .map((e) => bean.AudioDevicesChangedEvent.from(e))
          // .listen(onDevicesChanged??_onDevicesChanged);
          .listen(_onDevicesChanged);
      _devicesSubs = _audioSession?.devicesStream
          .map((e) => e.map((it) => bean.AudioDevice.from(it)).toSet())
          // .listen(onDevices??_onDevices);
          .listen(_onDevices);

    }

    onDevicesChangedList.add(onDevicesChanged);
    onDevicesList.add(onDevices);

  }

  void _onDevicesChanged(bean.AudioDevicesChangedEvent event){
    Log.log('Devices added:   ${event.devicesAdded}');
    Log.log('Devices removed: ${event.devicesRemoved}');
  }

  void _onDevices(Set<bean.AudioDevice> devices){
    Log.log('_onDevices');
    for (var it in devices) {
      Log.log('$it');
    }

    for (var it in onDevicesList) {
      it?.call(devices);
    }
  }

  Future<void> getAudioDevices() async{
    // Log.log('_audioSession $_audioSession');
    Set<AudioDevice>? devices = await _audioSession?.getDevices();
    // Log.log(devices);
    Log.log('getDevices');
    if(devices != null){
      // (_customOnDevices??_onDevices).call(devices
      //     .map((it) => bean.AudioDevice.from(it)).toSet()
      // );

      (_onDevices).call(devices
          .map((it) => bean.AudioDevice.from(it)).toSet()
      );
    }
    
  }

  Future<void> close() async{
    await _devicesChangedSubs?.cancel();
    await _devicesSubs?.cancel();
  }
}